#define QT_USE_FAST_CONCATENATION

#include "GLWidget.h"
#include "qevent.h"

GLWidget::GLWidget(const QGLFormat & format, QWidget *parent)
	: QGLWidget(format, parent)
{
	viewMatrix.translate(0, 0, -5);
	projectionMatrix.perspective(60, 1, 1, 10);

	//setMinimumSize(200, 200);
	//setWindowTitle(tr("PUMA"));
}


GLWidget::~GLWidget(void)
{
}

QSize GLWidget::minimumSizeHint() const
{
    return QSize(50, 50);
}

QSize GLWidget::sizeHint() const
{
    return QSize(400, 400);
}

void GLWidget::setXRotation(int angle)
{
	QVector4D camPos = viewMatrix * QVector4D(0,0,0,1);
	viewMatrix.translate(-camPos.toVector3D());
	viewMatrix.rotate(angle/10.0, 1.0, 0.0, 0.0);
	viewMatrix.translate(camPos.toVector3D());
	update();
}

void GLWidget::setYRotation(int angle)
{ 
	QVector4D camPos = viewMatrix * QVector4D(0,0,0,1);
	viewMatrix.translate(-camPos.toVector3D());
	viewMatrix.rotate(angle/10.0, 0.0, 1.0, 0.0);
	viewMatrix.translate(camPos.toVector3D());

	update();
}

void GLWidget::setZRotation(int angle)
{
	viewMatrix.rotate(angle/10.0, 0.0, 0.0, 1.0);
	update();
}

 void GLWidget::initializeGL()
 {
	qglClearColor(Qt::darkBlue);

	glEnable(GL_DEPTH_TEST);
	glEnable(GL_CULL_FACE);
	glShadeModel(GL_SMOOTH);
	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);
	glEnable(GL_MULTISAMPLE);
	static GLfloat lightPosition[4] = { 0.5, 5.0, 7.0, 1.0 };
	glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);

	if(prepareShaders() == false)
	{
		QMessageBox::warning(this, tr("Shaders preparation"),
			tr("Shaders loading failed!"), QMessageBox::Ok,
                                QMessageBox::Ok);
	}
 }

 void GLWidget::paintGL()
 {
     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	 
	 puma.DrawShader(mShaderProgram, viewMatrix, projectionMatrix);
 }

 void GLWidget::resizeGL(int width, int height)
 {
     int side = qMin(width, height);
     glViewport((width - side) / 2, (height - side) / 2, side, side);
 }

 void GLWidget::mousePressEvent(QMouseEvent *event)
 {
     lastPos = event->pos();
 }

 void GLWidget::mouseMoveEvent(QMouseEvent *event)
 {
	int dx = event->x() - lastPos.x();
	int dy = event->y() - lastPos.y();

	if (event->buttons() & Qt::LeftButton) {
		setXRotation(dy);
		setYRotation(dx);
	} else if (event->buttons() & Qt::RightButton) {
		setXRotation(dy);
		setZRotation(dx);
	}
	lastPos = event->pos();
 }


void GLWidget::keyPressEvent(QKeyEvent * event)
{
	float step = 0.1f;
	QVector4D direction = (viewMatrix * QVector4D(0,0,1,1)).normalized();
	switch(event->key())
	{
	case Qt::Key_W:
		viewMatrix.translate(-step * direction.toVector3D());
		break;
	case Qt::Key_A:
		
		break;
	case Qt::Key_S:
		viewMatrix.translate(step * direction.toVector3D());
		break;
	case Qt::Key_D:

		break;
	}
	updateGL();
}

void GLWidget::wheelEvent ( QWheelEvent * event )
{
	int numDegrees = event->delta() / 8;
	int numSteps = numDegrees / 15;

	if (event->orientation() == Qt::Horizontal) {
			update();
	} else {
			update();
	}
	event->accept();
}

bool GLWidget::prepareShaders()
{
	if(initShader(mShaderProgram, 
			QString("./shaders/shader_frag.fp"), 
			QString("./shaders/shader_vert.vp"), 
			QString("./shaders/geom.geom")) == false)
		return false;
	return true;
}

bool GLWidget::initShader(QGLShaderProgram& shader, const QString& fragFile, const QString& vertFile, const QString& geomFile)
{
		QGLShader shaderVert(QGLShader::Vertex);
		QGLShader shaderFrag(QGLShader::Fragment);
		QGLShader shaderGeom(QGLShader::Geometry);
		QString s = QFSFileEngine::currentPath();

		bool isCompiledGeometry, isCompiledVert, isCompiledFrag;
		bool isAddVert, isAddFrag, isAddGeom;

		if(geomFile.length() != 0)
		{
			isCompiledGeometry = shaderGeom.compileSourceFile(geomFile);
			s = shaderGeom.log();
			isAddGeom = shader.addShader(&shaderGeom);
		}
		isCompiledVert = shaderVert.compileSourceFile(vertFile);
		s = shaderVert.log();
		isCompiledFrag = shaderFrag.compileSourceFile(fragFile);
		s = shaderFrag.log();

		if(isCompiledVert && isCompiledFrag && isCompiledGeometry)
		{
			isAddVert = shader.addShader(&shaderVert);
			isAddFrag = shader.addShader(&shaderFrag);
			

			if(isAddVert && isAddFrag && isAddGeom)
			{
				shader.link();
				s = shader.log();
				return true;
			}
	}

	return false;
}

// draw the scene
void GLWidget::draw() 
{

}